﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
using System.Windows.Media.Media3D;
using System.Windows.Media;
using System.Windows.Controls;

namespace Eniux.Windows.ThreeDEngine.Helper
{
    /// <summary>
    /// Visual3D对象帮助类
    /// </summary>
    public static class Visual3DHelper
    {
        #region 常量、字段

        /// <summary>
        /// Visual3DModel 属性信息
        /// </summary>
        private static readonly PropertyInfo Visual3DModelPropertyInfo = typeof(Visual3D).GetProperty(
            "Visual3DModel", BindingFlags.Instance | BindingFlags.NonPublic);

        #endregion

        #region 公开的方法

        /// <summary>
        /// 查找指定类型对象的孩子
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="parent"></param>
        /// <returns></returns>
        public static T Find<T>(DependencyObject parent) where T : DependencyObject
        {
            // todo: this should be improved
            foreach (DependencyObject d in LogicalTreeHelper.GetChildren(parent))
            {
                var a = Find<T>(d);
                if (a != null)
                {
                    return a;
                }
            }

            var model = parent as ModelVisual3D;
            if (model != null)
            {
                var modelgroup = model.Content as Model3DGroup;
                if (modelgroup != null)
                {
                    return modelgroup.Children.OfType<T>().FirstOrDefault();
                }
            }

            return null;
        }

        /// <summary>
        /// 查找Visual3D对象的边界框
        /// </summary>
        /// <param name="children">Visual3D的子元素集合</param>
        /// <returns>三维矩形</returns>
        public static Rect3D FindBounds(Visual3DCollection children)
        {
            var bounds = Rect3D.Empty;
            foreach (var visual in children)
            {
                var b = FindBounds(visual, Transform3D.Identity);
                bounds.Union(b);
            }

            return bounds;
        }

        /// <summary>
        /// 查找指定Visual3D对象的边界框
        /// </summary>
        /// <param name="visual">Visual3D对象</param>
        /// <param name="transform">3维转换</param>
        /// <returns>三维矩形</returns>
        public static Rect3D FindBounds(Visual3D visual, Transform3D transform)
        {
            var bounds = Rect3D.Empty;
            var childTransform = Transform3DHelper.CombineTransform(visual.Transform, transform);
            var model = GetModel(visual);
            if (model != null)
            {
                // apply transform
                var transformedBounds = childTransform.TransformBounds(model.Bounds);
                if (!double.IsNaN(transformedBounds.X))
                {
                    bounds.Union(transformedBounds);
                }
            }

            foreach (var child in GetChildren(visual))
            {
                var b = FindBounds(child, childTransform);
                bounds.Union(b);
            }

            return bounds;
        }

        /// <summary>
        /// 获取Visual3D对象的3D空间转换矩阵
        /// </summary>
        /// <param name="visual"></param>
        /// <returns>3D空间转换矩阵</returns>
        public static Matrix3D GetTransform(Visual3D visual)
        {
            var totalTransform = Matrix3D.Identity;

            DependencyObject obj = visual;
            while (obj != null)
            {
                var viewport3DVisual = obj as Viewport3DVisual;
                if (viewport3DVisual != null)
                {
                    return totalTransform;
                }

                var mv = obj as ModelVisual3D;
                if (mv != null)
                {
                    if (mv.Transform != null)
                    {
                        totalTransform.Append(mv.Transform.Value);
                    }
                }

                obj = VisualTreeHelper.GetParent(obj);
            }

            throw new InvalidOperationException("The visual is not added to a Viewport3D.");
        }

        /// <summary>
        /// 获取指定Visual3D对象的Viewport3D
        /// </summary>
        /// <param name="visual">Visual3D对象</param>
        /// <returns>3D视窗Viewport3D</returns>
        public static Viewport3D GetViewport3D(Visual3D visual)
        {
            DependencyObject obj = visual;
            while (obj != null)
            {
                var vis = obj as Viewport3DVisual;
                if (vis != null)
                {
                    return VisualTreeHelper.GetParent(obj) as Viewport3D;
                }

                obj = VisualTreeHelper.GetParent(obj);
            }

            return null;
        }

        /// <summary>
        /// 获取视窗的3D空间转换矩阵
        /// </summary>
        /// <param name="visual">Visual3D对象</param>
        /// <returns>3D空间转换矩阵</returns>
        public static Matrix3D GetViewportTransform(Visual3D visual)
        {
            var totalTransform = Matrix3D.Identity;

            DependencyObject obj = visual;
            while (obj != null)
            {
                var viewport3DVisual = obj as Viewport3DVisual;
                if (viewport3DVisual != null)
                {
                    var matxViewport = Viewport3DHelper.GetTotalTransform(viewport3DVisual);
                    totalTransform.Append(matxViewport);
                    return totalTransform;
                }

                var mv = obj as ModelVisual3D;
                if (mv != null)
                {
                    if (mv.Transform != null)
                    {
                        totalTransform.Append(mv.Transform.Value);
                    }
                }

                obj = VisualTreeHelper.GetParent(obj);
            }

            throw new InvalidOperationException("The visual is not added to a Viewport3D.");

            // At this point, we know obj is Viewport3DVisual
        }

        /// <summary>
        /// 是否将Visual3D对象附加到3D视窗
        /// </summary>
        /// <param name="visual">要附加Visual3D对象</param>
        /// <returns>是或否</returns>
        public static bool IsAttachedToViewport3D(Visual3D visual)
        {
            DependencyObject obj = visual;
            while (obj != null)
            {
                var vis = obj as Viewport3DVisual;
                if (vis != null)
                {
                    return true;
                }

                obj = VisualTreeHelper.GetParent(obj);
            }

            return false;
        }

        /// <summary>
        /// 遍历Visual3D/Model3D树，运行指定的三维转换
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="visuals">Visual3D对象集合</param>
        /// <param name="action"></param>
        public static void Traverse<T>(Visual3DCollection visuals, Action<T, Transform3D> action) where T : Model3D
        {
            foreach (var child in visuals)
            {
                Traverse(child, action);
            }
        }

        /// <summary>
        /// 遍历Visual3D/Model3D树，运行指定的三维转换
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="visual">Visual3D对象</param>
        /// <param name="action"></param>
        public static void Traverse<T>(Visual3D visual, Action<T, Transform3D> action) where T : Model3D
        {
            Traverse(visual, Transform3D.Identity, action);
        }

        /// <summary>
        /// 遍历Model3D树，运行指定的三维转换
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="model">3D模型</param>
        /// <param name="action"></param>
        public static void TraverseModel<T>(Model3D model, Action<T, Transform3D> action) where T : Model3D
        {
            TraverseModel(model, Transform3D.Identity, action);
        }

        /// <summary>
        /// 遍历Model3D树，运行指定的三维转换
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="model">3D模型</param>
        /// <param name="transform">三维转换</param>
        /// <param name="action"></param>
        public static void TraverseModel<T>(Model3D model, Transform3D transform, Action<T, Transform3D> action)
            where T : Model3D
        {
            var mg = model as Model3DGroup;
            if (mg != null)
            {
                var childTransform = Transform3DHelper.CombineTransform(model.Transform, transform);
                foreach (var m in mg.Children)
                {
                    TraverseModel(m, childTransform, action);
                }
            }

            var gm = model as T;
            if (gm != null)
            {
                var childTransform = Transform3DHelper.CombineTransform(model.Transform, transform);
                action(gm, childTransform);
            }
        }

        #endregion

        #region 私有的方法

        /// <summary>
        /// 获取Visual3D对象的所有孩子
        /// </summary>
        /// <param name="visual">Visual3D对象</param>
        /// <returns>孩子对象集合</returns>
        private static IEnumerable<Visual3D> GetChildren(Visual3D visual)
        {
            int n = VisualTreeHelper.GetChildrenCount(visual);
            for (int i = 0; i < n; i++)
            {
                var child = VisualTreeHelper.GetChild(visual, i) as Visual3D;
                if (child == null)
                {
                    continue;
                }

                yield return child;
            }
        }

        /// <summary>
        /// 获取Visual3D对象的3D模型
        /// </summary>
        /// <param name="visual">Visual3D对象</param>
        /// <returns></returns>
        private static Model3D GetModel(Visual3D visual)
        {
            Model3D model;
            var mv = visual as ModelVisual3D;
            if (mv != null)
            {
                model = mv.Content;
            }
            else
            {
                model = Visual3DModelPropertyInfo.GetValue(visual, null) as Model3D;
            }

            return model;
        }

        /// <summary>
        /// 遍历指定的Visual3D对象
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="visual">Visual3D对象</param>
        /// <param name="transform">3D空间转换</param>
        /// <param name="action"></param>
        private static void Traverse<T>(Visual3D visual, Transform3D transform, Action<T, Transform3D> action)
            where T : Model3D
        {
            var childTransform = Transform3DHelper.CombineTransform(visual.Transform, transform);
            var model = GetModel(visual);
            if (model != null)
            {
                TraverseModel(model, childTransform, action);
            }

            foreach (var child in GetChildren(visual))
            {
                Traverse(child, childTransform, action);
            }
        }

        #endregion
    }
}
